use std::{
    fs,
    path::{Path, PathBuf},
};

use anyhow::{Context, Result};
use chrono::{Duration, Local, Utc};
use regex::Regex;

use crate::domain::{AcousticFileInfo, FileTimeScope};

#[allow(dead_code)]
pub struct TimeScopeService;

impl Default for TimeScopeService {
    fn default() -> Self {
        Self
    }
}

#[allow(dead_code)]
impl TimeScopeService {
    pub fn collect_acoustic_file_scopes(
        folder_path: impl AsRef<Path>,
        smprate: usize,
        channels: usize,
        depth: usize,
        offset: f64,
    ) -> Result<Vec<FileTimeScope>> {
        let folder_path = folder_path.as_ref();
        let bin_files = Self::collect_bin_files(folder_path)?;
        let mut scopes = Vec::with_capacity(bin_files.len());

        for file_path in bin_files {
            if let Some(info) =
                Self::parse_acoustic_file_info(&file_path, smprate, channels, depth)?
            {
                let start_ts = info.start_time.timestamp() as f64 + offset;
                let end_ts = start_ts + info.duration;
                scopes.push(FileTimeScope {
                    start_time: start_ts,
                    start_time_format: info
                        .start_time
                        .with_timezone(&Local)
                        .format("%Y-%m-%d %H:%M:%S")
                        .to_string(),
                    end_time: end_ts,
                    end_time_format: info
                        .end_time
                        .with_timezone(&Local)
                        .format("%Y-%m-%d %H:%M:%S")
                        .to_string(),
                    duration: info.duration,
                    file_path: info.file_path,
                    is_empty: info.duration <= 0.0,
                });
            }
        }

        scopes.sort_by(|a, b| a.start_time.partial_cmp(&b.start_time).unwrap());
        Ok(scopes)
    }

    fn collect_bin_files(folder_path: &Path) -> Result<Vec<PathBuf>> {
        if !folder_path.exists() {
            return Ok(Vec::new());
        }
        let mut files = Vec::new();
        for entry in fs::read_dir(folder_path)
            .with_context(|| format!("遍历目录失败: {}", folder_path.display()))?
        {
            let entry = entry?;
            let path = entry.path();
            if path.is_file() && path.extension().and_then(|e| e.to_str()) == Some("bin") {
                files.push(path);
            }
        }
        Ok(files)
    }

    fn parse_acoustic_file_info(
        file_path: &Path,
        smprate: usize,
        channels: usize,
        depth: usize,
    ) -> Result<Option<AcousticFileInfo>> {
        let file_name = match file_path.file_name().and_then(|s| s.to_str()) {
            Some(name) => name.to_string(),
            None => return Ok(None),
        };

        let regex = Regex::new(
            r"^save,(\d{4}-\d{2}-\d{2} \d{2}-\d{2}-\d{2}),(ip\d+\.\d+\.\d+\.\d+),(\d+)CH,(\d+)(kHz|Hz),(\d+)bit(?:,(\d+(?:\.\d+)?)V)?\.bin$",
        )
        .unwrap();

        let caps = match regex.captures(&file_name) {
            Some(caps) => caps,
            None => return Ok(None),
        };

        let time_str = caps.get(1).unwrap().as_str();
        let ip_identifier = caps.get(2).unwrap().as_str().to_string();
        let channels_from_name = caps
            .get(3)
            .unwrap()
            .as_str()
            .parse::<usize>()
            .unwrap_or(channels);
        let rate_value = caps.get(4).unwrap().as_str().parse::<usize>().unwrap_or(0);
        let rate_unit = caps.get(5).unwrap().as_str();
        let bit_depth = caps
            .get(6)
            .unwrap()
            .as_str()
            .parse::<usize>()
            .unwrap_or(depth);
        let voltage = caps.get(7).and_then(|m| m.as_str().parse::<f32>().ok());

        let mut sampling_rate = if rate_unit.eq_ignore_ascii_case("kHz") {
            rate_value * 1000
        } else {
            rate_value
        };
        if sampling_rate == 0 {
            sampling_rate = smprate;
        }

        let start_time = AcousticFileInfo::parse_time(time_str).unwrap_or_else(|| Utc::now());
        let file_size = file_path.metadata().map(|m| m.len()).unwrap_or(0);

        let bytes_per_sample = (bit_depth as f64) / 8.0;
        let total_samples =
            file_size as f64 / ((channels_from_name as f64) * bytes_per_sample).max(1.0);
        let duration = if sampling_rate > 0 {
            total_samples / (sampling_rate as f64)
        } else {
            0.0
        };

        let duration_ms = (duration * 1000.0).round() as i64;
        let end_time = start_time + Duration::milliseconds(duration_ms);
        let ip_address = ip_identifier.trim_start_matches("ip").to_string();

        Ok(Some(AcousticFileInfo {
            file_name,
            file_path: file_path.to_string_lossy().to_string(),
            start_time,
            end_time,
            ip_identifier,
            ip_address,
            channels: channels_from_name,
            sampling_rate,
            bit_depth,
            voltage_range: voltage,
            file_size,
            duration,
        }))
    }
}
